[ExecuTorch][WebGPU] et_vk conv2d: route standard (groups==1) conv through an im2col tiled GEMM (1.1-2.4x)#20873
[ExecuTorch][WebGPU] et_vk conv2d: route standard (groups==1) conv through an im2col tiled GEMM (1.1-2.4x)#20873JCNTH wants to merge 1 commit into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/20873
Note: Links to docs will display an error until the docs builds have been completed. ✅ You can merge normally! (1 Unrelated Failure)As of commit 0a73e83 with merge base aceeb40 ( BROKEN TRUNK - The following job failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
Stack from ghstack (oldest at bottom):
Problem: the direct conv2d kernel runs one thread per output element and re-reads the input receptive field from global memory for every output — zero cross-thread reuse. For the patch-embed stem (3-channel RGB) the vec4-over-IC path is inert (icpg=3 fails the
%4gate), so it runs the scalar direct path with no reuse at all.Solution: route groups==1 non-transposed convs through an implicit-im2col tiled GEMM that reuses the linear tiled-GEMM skeleton — M=OC, N=BOHOW, K=ICKHKW; shared-memory 32x32 tiles + 4x4 register blocking; the input is im2col-sampled on the fly (out-of-range -> 0.0 implements padding). Grouped/depthwise/transpose stay on the direct/gather kernels.
Before: every conv -> direct kernel (scalar, or vec4-over-IC when icpg%4==0), no input reuse.
After: groups==1 ->
conv2d_gemm(shared-mem tiling + register blocking, input-tile reuse across output positions); grouped/transpose -> unchanged.Implementation:
conv2d_gemm.wgsl(+ generated header): forkslinear_fp32_tiled.wgsl—read_aloads the weight[OC, K],read_bim2col-samples the input (decodes n->(b,oh,ow), kk->(ic,kh,kw); ih=ohsH-pH+khdH; bounds-check->0), bias per-row (OC), output written NCHW. Reuses the existingConvParamsuniform.Conv2d.cppbranches ongroups==1: GEMM viacompute_tile_grid_2d+add_dispatch_2d(mirrorsLinearFp32.cpp); else the existing direct dispatch. The grouped path is byte-identical; both grids are computed before any buffer alloc (throw-before-leak). Mirrors Vulkan's ownshould_use_conv2d_im2colgroups==1 routing.Constraints: scalar GEMM (no vec4) — NCHW's channel stride isn't contiguous, so vec4-over-K would be a strided gather (no compute win on Apple's scalar ALU); ORT skips vec4 for NCHW too.
Co-authored-with: Claude Code.
Differential Revision: D110995347